package com.bwie.system.service.impl;

import java.io.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

import org.apache.commons.io.FileUtils;

import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import com.bwie.common.domain.Data1;
import com.bwie.common.domain.Data2;
import com.bwie.common.domain.Data3;
import com.bwie.system.mapper.Data1Mapper;
import com.bwie.system.mapper.Data2Mapper;
import com.bwie.system.mapper.Data3Mapper;
import com.bwie.system.service.DataService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Sort;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.w3c.dom.*;

import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;


/**
 * @ClassName:
 * @Description:
 * @Author: zengfanzhen
 * @Date: 2024/4/13 16:05:38
 */
@Service
@Slf4j
public class DataServiceImpl implements DataService {

    @Autowired // 自动注入Data1Mapper实例
    private Data1Mapper data1Mapper;

    @Autowired // 自动注入Data2Mapper实例
    private Data2Mapper data2Mapper;

    @Autowired // 自动注入Data3Mapper实例
    private Data3Mapper data3Mapper;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;


    @Override
    public void produceData() {
        // 2.6.1.1 去redis根据"dataLastTime"查询一个数据的最新时间
        ValueOperations<String, String> valueOps = redisTemplate.opsForValue();
        String lastTime = valueOps.get("dataLastTime");

        // 2.6.1.2 如果为空，查询data1的全部数据；如果不为空，将这个时间作为查询条件查询data1，并根据时间字段进行排序，得到data1List
        List<Data1> data1s;
        if (lastTime != null) {
            // 解析时间字符串，根据时间字段进行排序查询data1
            Date time = parseTime(lastTime); // 请替换parseTime为你的时间解析方法
            Example<Data1> example = Example.of(new Data1(null, null, null, null, time));
            Sort sort = Sort.by(Sort.Order.asc("time")); // 假设时间字段为time，请根据实际字段名修改
            data1s = data1Mapper.selectAll();
        } else {
            // 查询data1的全部数据
            data1s = data1Mapper.selectAll();
        }

//        List<Data1> data1s = data1Mapper.selectAll();

        for (Data1 data1 : data1s) {
            // 异步查询 data2
            CompletableFuture<Data2> data2Future = CompletableFuture.supplyAsync(() -> data2Mapper.selectById(data1.getId()));
            // 异步查询 data3
            CompletableFuture<Data3> data3Future = CompletableFuture.supplyAsync(() -> data3Mapper.selectById(data1.getId()));

            CompletableFuture<Void> combinedFuture = data2Future.thenCombine(data3Future, (data2, data3) -> {
                if (data2 != null && data3 != null) {
                    // 如果查询到的 data2 和 data3 都不为空，则处理获取到的 data2 和 data3
                    String imageName1 = saveImageAndReturnName(data2.getPhoto1());
                    String imageName2 = saveImageAndReturnName(data2.getPhoto2());

                    String bcpContent = generateBcpEntry(data1, data2, data3, imageName1, imageName2);

                    // 生成BCP文件名
                    String bcpFileName = generateBcpFileName();

                    //指定文件夹
                    String filePath = "E:\\4G\\";

                    // 指定BCP文件的完整路径
                    String fileBcpPath = filePath + bcpFileName;

                    // 将BCP内容写入到指定路径的文件中
                    writeBcpToFile(fileBcpPath, bcpContent);

                    // 打印日志，提示BCP文件已成功写入
                    log.info("BCP文件已成功写入到：" + fileBcpPath);
                    // 文件夹路径
                    String fileXmlPath = "D:\\QQ\\97. 4G文件落地\\310000-310000-1698848320-DATANAME-00633\\JZ_ZIP_INDEX.xml"; // 原始XML文件路径

                    // 替换XML文件中的内容并写入新的文件
                    getXmlFile(bcpFileName, fileXmlPath, filePath);

                    // 指定压缩后存放的文件夹
                    String zipFilePath = "E:\\97.4G\\";

                    // 指定压缩文件的名称
                    String zipFileName = "4G.zip";

                    // 完整的压缩文件路径
                    String fullZipFilePath = zipFilePath + zipFileName;

                    createDirectoryIfNotExists(zipFilePath);
                    zipFiles(filePath, fullZipFilePath); // 压缩文件夹并删除原始文件夹

                    // 调用私有方法删除文件夹
                    deleteFolder(filePath);


                    if (!data1s.isEmpty()) {
                        Data1 latestData = null;
                        Date latestTime = null;
                        // 找到最新的时间
                        for (Data1 data : data1s) {
                            if (latestTime == null || data.getTime().after(latestTime)) {
                                latestData = data;
                                latestTime = data.getTime();
                            }
                        }
                        if (latestData != null) {
                            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                            String formattedDate = dateFormat.format(latestTime);
                            // 将最新时间存入 Redis，并设置过期时间为半小时
                            redisTemplate.opsForValue().set("lastTime", formattedDate, 30, TimeUnit.MINUTES);
                        }
                    }


                } else {
                    // 如果查询到的 data2 或 data3 为空，则记录警告日志或采取其他处理
                    log.info("根据 Data1 的 id 查询到的 Data2 或 Data3 为空，id: " + data1.getId());
                }
                return null;
            });
        }
    }


    // 2.6.1.6.编写图片保存方法，将图片存储到本地路径 ，并返回图片名称
    // 保存图片并返回图片名称
    public String saveImageAndReturnName(byte[] base64Image) {
        try {

            // 生成图片文件名
            String imageName = generateImageName();

            // 将字节数组写入图片文件
            String imagePath = "E:\\4G\\" + imageName; // 保存图片的路径
            writeImageToFile(base64Image, imagePath);
            log.info("图片已成功保存到：" + imagePath);

            return imageName;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    // 生成图片文件名
    private static String generateImageName() {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
        return "image_" + dateFormat.format(new Date()) + ".jpg";
    }

    // 将字节数组写入图片文件
    private static void writeImageToFile(byte[] imageData, String imagePath) throws IOException {
        Path path = Paths.get(imagePath);
        try (FileOutputStream fos = new FileOutputStream(path.toFile())) {
            fos.write(imageData);
        }
    }

    // 2.6.1.7.根据data1、2、3及返回的图片名称，拼接字符串写入到bcp文件中，并返回文件内容
    private String generateBcpEntry(Data1 data1, Data2 data2, Data3 data3, String imageName1, String imageName2) {
        StringBuilder bcpEntry = new StringBuilder();

        // 拼接data1字段
        bcpEntry.append(data1.getId()).append("\t");
        bcpEntry.append(data1.getXM()).append("\t");
        bcpEntry.append(data1.getCard()).append("\t");
        bcpEntry.append(data1.getAge()).append("\t");
        bcpEntry.append(data1.getTime()).append("\t");

        // 拼接data2字段
        bcpEntry.append(data2.getId()).append("\t");
        bcpEntry.append(data2.getXM()).append("\t");
        bcpEntry.append(data2.getCard()).append("\t");
        bcpEntry.append(data2.getPhoto1()).append("\t");
        bcpEntry.append(data2.getPhoto2()).append("\t");

        // 拼接data3字段
        bcpEntry.append(data3.getId()).append("\t");
        bcpEntry.append(data3.getPlate()).append("\t");
        bcpEntry.append(data3.getTime()).append("\t");

        // 拼接图片名称
        bcpEntry.append(imageName1).append("\t");
        bcpEntry.append(imageName2).append("\n");

        return bcpEntry.toString(); // 返回拼接的字符串
    }


    // 2.6.1.8.生成BCP文件名
    private String generateBcpFileName() {
        String dataCollectSystemCode = "50019";
        String dataOriginCode = "310000";
        String dataTypeCode = "DATA_SATELLITE_MOBILE_CALL";
        long absoluteSeconds = Instant.now().getEpochSecond();
        String sequenceNumber = String.format("%05d", getNextSequenceNumber()); // 生成5位自增序列号

        return String.format("%s-%s-%s-%d-%s.bcp", dataCollectSystemCode, dataOriginCode, dataTypeCode, absoluteSeconds, sequenceNumber);
    }

    // 2.6.1.8.获取下一个自增序列号
    private static int getNextSequenceNumber() {
        return 598; // 示例返回值，需要根据实际逻辑修改
    }

    private static void writeBcpToFile(String filePath, String content) {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) {
            writer.write(content); // 将内容写入文件
            log.info("已生成 BCP 文件： " + filePath);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    // 时间字符串解析方法，请根据实际需求自行实现
    private Date parseTime(String timeStr) {
        // 请根据你的时间字符串解析逻辑实现该方法
        return null;
    }


    public static void getXmlFile(String bcpFileName, String fileXmlPath, String filePath) {

        try {
            // 读取XML文件
            Document document = parseXMLFile(fileXmlPath);
            // 替换XML文件中的文件名
            replaceFileName(document, bcpFileName);
            // 将修改后的XML文件写入新文件中
            writeXMLFile(document, filePath);
            // 输出成功信息
            System.out.println("XML文件内容已更新并写入成功！");
        } catch (Exception e) {
            // 打印错误日志
            e.printStackTrace();
        }
    }


    // 读取XML文件并返回Document对象
    public static Document parseXMLFile(String filePath) throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        return builder.parse(new File(filePath));
    }

    // 替换XML文件中的文件名
    public static void replaceFileName(Document document, String bcpFileName) {
        NodeList nodeList = document.getElementsByTagName("ITEM");
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node node = nodeList.item(i);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                Element element = (Element) node;
                String engValue = element.getAttribute("eng");
                if ("FILE_NAME".equals(engValue)) {
                    element.setAttribute("val", bcpFileName);
                    break; // 只需要替换第一个匹配的值，因此找到后就可以退出循环
                }
            }
        }
    }

    // 将Document对象写入XML文件
    public static void writeXMLFile(Document document, String folderPath) throws Exception {
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        DOMSource source = new DOMSource(document);
        File file = new File(folderPath, "JZ_ZIP_INDEX_modified.xml"); // 新文件路径
        File parentDir = file.getParentFile();
        if (!parentDir.exists()) {
            parentDir.mkdirs(); // 创建文件的父目录及其所有祖先目录
        }
        StreamResult result = new StreamResult(file);
        transformer.transform(source, result);
    }

    public static void main(String[] args) {
        String originalFilePath = "D:\\QQ\\97. 4G文件落地\\310000-310000-1698848320-DATANAME-00633\\JZ_ZIP_INDEX.xml"; // 原始XML文件路径
        String folderPath = "E:\\4G"; // 新文件夹路径
        String bcpFileName = "example.bcp"; // 替换信息

        try {
            // 读取XML文件
            Document document = parseXMLFile(originalFilePath);
            // 替换XML文件中的文件名
            replaceFileName(document, bcpFileName);
            // 将修改后的XML文件写入新文件中
            writeXMLFile(document, folderPath);
            // 输出成功信息
            System.out.println("XML文件内容已更新并写入成功！");
        } catch (Exception e) {
            // 打印错误日志
            e.printStackTrace();
        }
    }


    // 调用私有方法压缩文件夹
    private static void zipFiles(String sourceFolderPath, String zipFilePath) {
        try (FileOutputStream fos = new FileOutputStream(zipFilePath);
             ZipOutputStream zos = new ZipOutputStream(fos)) {

            File sourceFolder = new File(sourceFolderPath);
            zipFile(sourceFolder, sourceFolder.getName(), zos); // 递归压缩文件夹中的文件

            log.info("文件成功压缩到: " + zipFilePath); // 记录日志
        } catch (IOException e) {
            log.error("压缩文件时发生错误: ", e); // 记录错误日志
        }
    }

    // 私有方法：递归压缩文件夹中的文件
    private static void zipFile(File fileToZip, String fileName, ZipOutputStream zipOut) throws IOException {
        if (fileToZip.isHidden()) {
            return;
        }
        if (fileToZip.isDirectory()) {
            File[] children = fileToZip.listFiles();
            for (File childFile : children) {
                zipFile(childFile, fileName + "/" + childFile.getName(), zipOut); // 递归压缩文件夹中的文件
            }
            return;
        }
        FileInputStream fis = new FileInputStream(fileToZip);
        ZipEntry zipEntry = new ZipEntry(fileName);
        zipOut.putNextEntry(zipEntry);
        byte[] bytes = new byte[1024];
        int length;
        while ((length = fis.read(bytes)) >= 0) {
            zipOut.write(bytes, 0, length);
        }
        fis.close();
    }

    // 私有方法：删除文件夹及其所有内容
    private static void deleteFolder(String folderPath) {
        try {
            FileUtils.deleteDirectory(new File(folderPath)); // 删除文件夹及其所有内容
            log.info("文件夹成功删除: " + folderPath); // 记录日志
        } catch (IOException e) {
            log.error("删除文件夹时发生错误: ", e); // 记录错误日志
        }
    }

    // 创建目标文件夹（如果不存在）
    private static void createDirectoryIfNotExists(String directoryPath) {
        File directory = new File(directoryPath);
        if (!directory.exists()) {
            directory.mkdirs(); // 创建目标文件夹及其所有父目录
        }
    }


}


